package com.i72.penetrate.rpc;

import com.i72.basic.tools.BeanHelper;
import com.i72.basic.context.GlobalContext;
import com.i72.basic.dynamic.JavassistCode;
import com.i72.governance.service.bean.ServiceInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Component
@Slf4j
public class HttpTarget implements RPCTarget {



    @Override
    public void initProxy(BeanFactory factory, String... packages) {


        List<Class> classList = BeanHelper.scanPackage(packages);

        //只要接口
        classList = classList.stream().filter(i->i.isInterface()).collect(Collectors.toList());

        String basePackage = GlobalContext.getAPPPackage();

        if(StringUtils.isEmpty(basePackage)){
            log.error("没有获取到基础包，无法生成代理类");
            return;
        }


        classList = excludeClassName(classList,basePackage);
        if(classList!=null && classList.size()>0){
            List<Class> newClassList = new ArrayList<>();
            for(Class c:classList){

                //动态代理
                Class proxyClass = buildProxy(c.getName());

                newClassList.add(proxyClass);

                String value = "";
            }

           registerBean(factory,newClassList);

        }


    }


    private void registerBean(BeanFactory factory,List<Class> classes){

        if(classes!=null && classes.size()>0) {

            //DefaultListableBeanFactory f = (DefaultListableBeanFactory)factory;
            for(Class c:classes) {
                //之前是懒加载
                BeanHelper.registerBean(factory,"",c.getSimpleName()+"init",false,false,c);
            }
        }
    }

    private Class buildProxy(String interfaceName){



        String newClassName = interfaceName+"$Proxy$Impl";
        String [] names = newClassName.split("\\.");
        String newName = names[names.length-1];
        JavassistCode code = JavassistCode.createClass(newClassName,RPCBase.class.getName());
        code.addInterface(interfaceName);

        try {
            Class c = Class.forName(interfaceName);

            Method [] ms = c.getDeclaredMethods();

            for(Method m:ms){


                //Object [] paramArr = new Object[]{interfaceName};

                //String params = code.anaylzeMethodParams(m);
                String methodCode = "";
                if(m.getParameterCount()<=0) {
                    methodCode = code.anaylzeMethod(m, "return this.request(\""+m.getName()+"\",null);");
                }else {

                    //List<String> invokeCode = new ArrayList<>();
                    String paramsTemplete = " Object [] paramArr = new Object[]{%s}; ";
                    Class [] paramsTypeList = m.getParameterTypes();
                    List<String> paramsList = new ArrayList<>();
                    for(int i=0;i<paramsTypeList.length;i++) {
                        paramsList.add("args"+i);
                    }
                    paramsTemplete = String.format(paramsTemplete,String.join(",",paramsList));

                    methodCode = code.anaylzeMethod(m, paramsTemplete+"return this.request(\""+m.getName()+"\",paramArr);");
                }
                code.addMethod(m.getName(),methodCode);
            }

            StringBuilder initCode = new StringBuilder();
            initCode.append("private void "+newName+"init(){ ");
            initCode.append("this.className =\""+c.getSimpleName()+"\"; ");
            initCode.append("this.classFullName = \""+c.getName()+"\"; ");
            initCode.append("this.initParams(); ");
            initCode.append("}");
            code.addMethod(newName+"init",initCode.toString());

            return code.toClass();


        }catch (Exception e){
            String value = "";
        }

        return null;
    }


    private List<Class> excludeClassName(List<Class> interfaceList,String basePackage){

        List<Class> newList = new ArrayList<>(interfaceList);

        if(interfaceList!=null && interfaceList.size()>0) {
            for (Class c : interfaceList) {
                if(c.getName().contains(basePackage)){
                    newList.remove(c);
                }
            }
        }
        return newList;

    }
}
